<?php


namespace frontend\controllers;


use common\helpers\DocParserFactory;
use common\helpers\DocReader;
use common\helpers\MyDocReader;
use common\models\c2\entity\Log;
use common\models\LoginForm;
use frontend\models\ClassA;
use frontend\models\ClassB;
use frontend\models\ClassC;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
use Yii;
use yii\db\Query;
use yii\di\Container;
use yii\helpers\ArrayHelper;
use yii\web\Controller;
use yii\web\Response;
use yii\web\User;

class TestController extends Controller
{

    public function actionKrajee()
    {
        Yii::trace('krajee visted', __METHOD__);
        $model = new LoginForm();
        return $this->render('krajee', ['model' => $model]);
    }

    /**
     * 构造方法注入
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function actionTest1()
    {
        $container = new Container();
        $classA = $container->get(ClassA::className());
        echo $classA->fuckOff();
    }

    /**
     * 方法注入
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function actionTest2()
    {
        $classC = new ClassC();
        echo Yii::$container->invoke([$classC, 'doThat'], ['param1' => 'fuck off']);
    }

    /**
     * PHP回调注册
     * 可以做一个Builder类代替回调 e.g. $container->set('Foo', ['common\hepler\FooBuilder', 'build'])
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function actionTest3()
    {
        $container = new Container();
        $container->set(ClassC::className(), function () {
            // new classC ...
        });

        $classC = $container->get(ClassC::className());
    }

    /*
     * 属性和方法注入
     */
    public function actionTest4()
    {
        // $container = new Container();
    }

    /**
     * 注册别名
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function actionTest5()
    {
        $container = new Container();
        $container->set('conn', 'yii\db\Connection');
        $conn = $container->get('conn');
        var_dump($conn);
    }

    /**
     * 注册依赖关系
     */
    public function actionTest6()
    {
        $container = new Container();

        // 注册一个接口
        // 当一个类以来这个接口时
        // 相应的类会被初始化作为依赖对下
        $container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');

        // 注册一个别名
        // 可以使用$container->get('conn')创建一个Connection实例
        $container->set('conn', 'yii\db\Connection');

        // set注册的依赖关系，在每次使用都会产生一个新实例
        // 下面方法注册一个单例依赖
        $container->setSingleton('yii\db\connection', [
            'dsn' => '',
            'username' => '',
            'password' => '',
            'charset' => '',
            // ...
        ]);
    }

    public function actionTest7()
    {
        // $count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM user')
        //     ->queryScalar();

        // $table = Yii::$app->db->getTableSchema('user');

        foreach (Log::find()->batch(10) as $items) {
            foreach ($items as $item) {
                var_dump($item->id);
            }
        }
    }

    public function actionTest8()
    {
        $ref = new \ReflectionClass(SiteController::className());

        $doc = $ref->getDocComment();
        var_dump($doc);
    }

    /**
     * 获取指定路径下所有全限定类名
     */
    public function actionTest9()
    {
        $path = Yii::getAlias('@app') . '\controllers';
        $fqcns = array();

        $allFiles = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
        $phpFiles = new RegexIterator($allFiles, '/\.php$/');


        foreach ($phpFiles as $phpFile) {
            $content = file_get_contents($phpFile->getRealPath());
            $tokens = token_get_all($content);
            $namespace = '';
            for ($index = 0; isset($tokens[$index]); $index++) {
                if (!isset($tokens[$index][0])) {
                    continue;
                }
                if (T_NAMESPACE === $tokens[$index][0]) {
                    $index += 2; // Skip namespace keyword and whitespace
                    while (isset($tokens[$index]) && is_array($tokens[$index])) {
                        $namespace .= $tokens[$index++][1];
                    }
                }
                if (T_CLASS === $tokens[$index][0]) {
                    $index += 2; // Skip class keyword and whitespace
                    $fqcns[] = $namespace . '\\' . $tokens[$index][1];
                }
            }
        }
        // var_dump($fqcns);
        return $fqcns;
    }

    /**
     * 获取类注释，需要传方法
     * @throws \ReflectionException
     */
    public function actionTest10()
    {
        $clazzs = $this->actionTest9();
        // $clazzs = [
        //     'frontend\controllers\DocController',
        //     'frontend\controllers\EsController',
        // ];
        $result = [];
        foreach ($clazzs as $clazz) {
            $classname = $clazz;
            $reflection = new \ReflectionClass($classname);
            $methods = $reflection->getMethods();
            $ownMethods = [];
            // 拿到只属于自己的方法
            foreach ($methods as $item) {
                if ($item->class == $classname) {
                    $ownMethods[] = $item;
                }
            }
            $parsed = [];
            // 解析每个方法注释
            foreach ($ownMethods as $item) {
                $reader = MyDocReader::getInstance();
                $doc = $reader->parsed($item->getDocComment());
                if (!empty($doc)) {
                    $parsed[] = $doc;
                }
            }
            $result[] = $parsed;
        }
        Yii::info($result);
        var_dump($result);
        exit;
    }

    public function actionTest11()
    {
        $classname = 'frontend\controllers\DocController';
        $reader = new DocReader($classname, 'postFormMethod');
    }

    /**
     * 获取类注释
     * @throws \ReflectionException
     */
    public function actionTest12()
    {

        $classname = 'frontend\controllers\DocController';

        $ref = new \ReflectionClass($classname);


        // 通过反射获取类的注释
        $doc = $ref->getDocComment();

        // 解析类的注释头
        $parase_result = DocParserFactory::getInstance()->parse($doc);
        $class_metadata = $parase_result;

        $methods = $ref->getMethods(
            \ReflectionMethod::IS_PUBLIC
            + \ReflectionMethod::IS_PROTECTED
            + \ReflectionMethod::IS_PRIVATE
        );

        // 过滤父类方法
        $ownMethods = [];
        foreach ($methods as $item) {
            if ($item->class == $classname) {
                $ownMethods[] = $item;
            }
        }

        $data = [];
        foreach ($ownMethods as $method) {
            // 获取方法的注释
            $doc = $method->getDocComment();
            // 解析注释
            $info = DocParserFactory::getInstance()->parse($doc);
            $metadata = $class_metadata + $info;
            // 获取方法的类型
            $method_flag = $method->isProtected();// 还可能是public，protected类型的
            // 获取方法的参数
            $params = $method->getParameters();
            $position = 0;    // 记录参数的次序
            foreach ($params as $param) {
                $arguments[$param->getName()] = $position;
                // 参数是否设置了默认参数，如果设置了，则获取其默认值
                $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
                $position++;
            }

            $call = array(
                'class_name' => $classname,
                'method_name' => $method->getName(),
                'arguments' => $arguments,
                'defaults' => $defaults,
                'metadata' => $metadata,
                'method_flag' => $method_flag
            );

            $data[] = $call;
        }
        // var_dump($data);
    }

    /**
     * 根据指定目录下获取所有php文件名方法注释
     * @return array
     */
    public function actionTest13()
    {
        $path = Yii::getAlias('@app') . '\controllers';
        $fqcns = array();

        $allFiles = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
        $phpFiles = new RegexIterator($allFiles, '/\.php$/');

        foreach ($phpFiles as $phpFile) {
            $content = file_get_contents($phpFile->getRealPath());
            $tokens = token_get_all($content);
            for ($index = 0; isset($tokens[$index]); $index++) {
                if (!isset($tokens[$index][0])) {
                    continue;
                }
                if (T_DOC_COMMENT === $tokens[$index][0]) {
                    if ($tokens[$index+2][0] != T_CLASS) {
                        $fqcns[] = $tokens[$index][1];
                    }
                }
            }
        }
        // var_dump($fqcns);
        // Yii::info($fqcns);
        return $fqcns;
    }

    public function actionTest14()
    {
        $exegs = $this->actionTest13();
        $result = [];
        foreach ($exegs as $exeg) {
            $parsed = [];
            // 解析每个方法注释
            $reader = MyDocReader::getInstance();
            $doc = $reader->parsed($exeg);
            if (!empty($doc)) {
                $parsed[] = $doc;
            }
            $result[] = $parsed;
        }
        // Yii::info($result);
        // var_dump($result);
        // exit;
    }

    public function actionTest15()
    {
        $doc = "/**
                * 接口名称
                *
                * 这是描述这是描述这是描述，这是描述
                * 多行描述
                * 很多行描述
                *
                * @namespace /ddd/fff/或中文 //目录结构，用于文档平台菜单等地方
                * @author chen //作者
                * @create_time 2019/9/19 //创建时间
                * @protocol http //请求协议
                * @host ms.geto.pro //接口官方域名，测试域名应由测试用例中自己修改
                * @url /site/post-form-method //请求资源地址
                * @method POST //请求类型
                * @header 键名1 键值1 描述描述
                *       描述描述描述描述描述描述
                *       很多很多描<a href=\"/ddd/fff/234\" target=\"_blank\">述描述描述</a>描述描述描述描述描述描述描述
                * @header 键名2 键值2 描述
                * @param_query string[len=10,30|required=1|default=123|function=signature_ms] 参数名 描述
                * @param_httpquery string[len=50|required=0] 参数名 描述
                * @param_httpquery string[len=10,] 参数名 描述 //中括号里不允许有空格
                * @param_httpquery mixed 参数名 描述
                * @param string 一维键[二维键1] 描述
                * @param float|int 一维键[二维键2] 描述
                * @param string|int 参数名 描述
                * @param string_json 参数名 描述 //json格式的字符串
                * @param file 参数名 描述 //一般是上传文件时，内容一般是二进制数据
                * @body_encode 方法名 //请求body格式化，发起请求前最后一步触发
                * @response_decode 解析方法名 //对响应结果做解析
                * @response_param string 返回参数名1 描述1
                * @response_param int 返回参数名2 描述2
                * @response_error 错误码1 描述
                * @response_error 错误码2 描述
                * @response_error 错误码3 描述
                * @demo 示例名 {request:xxx,response:xxx}
                * @demo_request demo1名 示例名 描述
                * @demo_request demo1名 示例名 描述
                * @demo_response demo1名 示例名 描述
                * @demo_request demo2名 示例名 描述
                * @demo_response demo2名 示例名 描述
                *
                */";
        // $info = DocParserFactory::getInstance()->parse($doc);
        $reader = MyDocReader::getInstance();
        $doc = $reader->parsed($doc);
    }

    public function actionTest16()
    {
        // $pattern = '/\bhi\b.*\bchen\b/';
        $pattern = '/\b([di]?)\b\s+\1\b/';
        $content = 'hi, chen, 早, chen.
        20189998989
        201
        di di
        hi, 今晚吃什么， chen
        hi, 今晚吃什么， chen';
        preg_match_all($pattern, $content, $matches);
        var_dump($matches);
    }

}